/* Note : this particular snipset of code is available under
 * the LGPL, MPL or BSD license (at your choice).
 * Jean II
 */

/* --------------------------- INCLUDE --------------------------- */

#define MAX_KEY_SIZE	16
#define	MAX_KEYS	8
int	key_on = 0;
int	key_open = 1;
int	key_current = 0;
char	key_table[MAX_KEYS][MAX_KEY_SIZE];
int	key_size[MAX_KEYS];

/* --------------------------- HANDLERS --------------------------- */

static int ioctl_set_encode(struct net_device *dev,
        struct iw_request_info *info,
        struct iw_point *erq,
        char *key)
{
    int	index = (erq->flags & IW_ENCODE_INDEX) - 1;

    if (erq->length > 0)
    {
        /* Check the size of the key */
        if(erq->length > MAX_KEY_SIZE)
            return(-EINVAL);

        /* Check the index */
        if((index < 0) || (index >= MAX_KEYS))
            index = key_current;

        /* Copy the key in the driver */
        memcpy(key_table[index], key, erq->length);
        key_size[index] = erq->length;
        key_on = 1;
    }
    else
    {
        /* Do we want to just set the current key ? */
        if((index >= 0) && (index < MAX_KEYS))
        {
            if(key_size[index] > 0)
            {
                key_current = index;
                key_on = 1;
            }
            else
                return(-EINVAL);
        }
    }

    /* Read the flags */
    if(erq->flags & IW_ENCODE_DISABLED)
        key_on = 0;		/* disable encryption */
    if(erq->flags & IW_ENCODE_RESTRICTED)
        key_open = 0;	/* disable open mode */
    if(erq->flags & IW_ENCODE_OPEN)
        key_open = 1;	/* enable open mode */

    return(0);
}

static int ioctl_get_encode(struct net_device *dev,
        struct iw_request_info *info,
        struct iw_point *erq,
        char *key)
{
    int	index = (erq->flags & IW_ENCODE_INDEX) - 1;

    /* Set the flags */
    erq->flags = 0;
    if(key_on == 0)
        erq->flags |= IW_ENCODE_DISABLED;
    if(key_open == 0)
        erq->flags |= IW_ENCODE_RESTRICTED;
    else
        erq->flags |= IW_ENCODE_OPEN;

    /* Which key do we want */
    if((index < 0) || (index >= MAX_KEYS))
        index = key_current;
    erq->flags |= index + 1;

    /* Copy the key to the user buffer */
    erq->length = key_size[index];
    memcpy(key, key_table[index], key_size[index]);

    return(0);
}

static int ioctl_get_range(struct net_device *dev,
        struct iw_request_info *info,
        struct iw_point *rrq,
        char *extra)
{
    struct iw_range *range = (struct iw_range *) extra;

    rrq->length = sizeof(struct iw_range);

    memset(range, 0, sizeof(struct iw_range));

#if WIRELESS_EXT > 10
    /* Version we are compiled with */
    range->we_version_compiled = WIRELESS_EXT;
    /* Minimum version we recommend */
    range->we_version_source = 8;
#endif /* WIRELESS_EXT > 10 */

#if WIRELESS_EXT > 8
    range->encoding_size[0] = 8;	/* DES = 64 bits key */
    range->encoding_size[1] = 16;
    range->num_encoding_sizes = 2;
    range->max_encoding_tokens = 8;
#endif /* WIRELESS_EXT > 8 */
    return(0);
}

/* --------------------------- BINDING --------------------------- */

#if WIRELESS_EXT > 12
static const iw_handler		handler_table[] =
{
    ...
        (iw_handler) ioctl_set_encode,		/* SIOCSIWENCODE */
    (iw_handler) ioctl_get_encode,		/* SIOCGIWENCODE */
};
#else	/* WIRELESS_EXT < 12 */
    static int
do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
    struct iwreq *wrq = (struct iwreq *) ifr;
    int		err = 0;

    switch (cmd)
    {
#if WIRELESS_EXT > 8
        case SIOCSIWENCODE:
            {
                char keybuf[MAX_KEY_SIZE];
                if(wrq->u.encoding.pointer)
                {
                    /* We actually have a key to set */
                    if(wrq->u.encoding.length > MAX_KEY_SIZE)
                    {
                        err = -E2BIG;
                        break;
                    }
                    if(copy_from_user(keybuf, wrq->u.encoding.pointer,
                                wrq->u.encoding.length))
                    {
                        err = -EFAULT;
                        break;
                    }
                }
                else
                    if(wrq->u.encoding.length != 0)
                    {
                        err = -EINVAL;
                        break;
                    }
                err = ioctl_set_encode(dev, NULL, &(wrq->u.encoding), keybuf);
            }
            break;

        case SIOCGIWENCODE:
            /* only super-user can see encryption key */
            if(! capable(CAP_NET_ADMIN))
            {
                err = -EPERM;
                break;
            }
            {
                char keybuf[MAX_KEY_SIZE];
                err = ioctl_get_encode(dev, NULL, &(wrq->u.encoding), keybuf);
                if(wrq->u.encoding.pointer)
                {
                    if (copy_to_user(wrq->u.encoding.pointer, keybuf,
                                wrq->u.encoding.length))
                        err= -EFAULT;
                }
            }
            break;
#endif	/* WIRELESS_EXT > 8 */
    }
    return(err);
}
#endif	/* WIRELESS_EXT < 12 */

